{
  "nbformat": 4,
  "nbformat_minor": 0,
  "metadata": {
    "colab": {
      "name": "custom_layers.ipynb",
      "version": "0.3.2",
      "provenance": [],
      "private_outputs": true,
      "collapsed_sections": [],
      "toc_visible": true
    },
    "language_info": {
      "codemirror_mode": {
        "name": "ipython",
        "version": 3
      },
      "file_extension": ".py",
      "mimetype": "text/x-python",
      "name": "python",
      "nbconvert_exporter": "python",
      "pygments_lexer": "ipython3",
      "version": "3.7.1"
    },
    "kernelspec": {
      "display_name": "Python 3",
      "language": "python",
      "name": "python3"
    }
  },
  "cells": [
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "tDnwEv8FtJm7"
      },
      "source": [
        "##### Copyright 2018 The TensorFlow Authors."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "cellView": "form",
        "colab_type": "code",
        "id": "JlknJBWQtKkI",
        "colab": {}
      },
      "source": [
        "#@title Licensed under the Apache License, Version 2.0 (the \"License\");\n",
        "# you may not use this file except in compliance with the License.\n",
        "# You may obtain a copy of the License at\n",
        "#\n",
        "# https://www.apache.org/licenses/LICENSE-2.0\n",
        "#\n",
        "# Unless required by applicable law or agreed to in writing, software\n",
        "# distributed under the License is distributed on an \"AS IS\" BASIS,\n",
        "# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n",
        "# See the License for the specific language governing permissions and\n",
        "# limitations under the License."
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "60RdWsg1tETW"
      },
      "source": [
        "# 사용자 정의 층"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "BcJg7Enms86w"
      },
      "source": [
        "<table class=\"tfo-notebook-buttons\" align=\"left\">\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://colab.research.google.com/github/tensorflow/docs/blob/master/site/ko/r1/tutorials/eager/custom_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/colab_logo_32px.png\" />구글 코랩(Colab)에서 실행하기</a>\n",
        "  </td>\n",
        "  <td>\n",
        "    <a target=\"_blank\" href=\"https://github.com/tensorflow/docs/blob/master/site/ko/r1/tutorials/eager/custom_layers.ipynb\"><img src=\"https://www.tensorflow.org/images/GitHub-Mark-32px.png\" />깃허브(GitHub) 소스 보기</a>\n",
        "  </td>\n",
        "</table>"
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "asCKfd--yH-6",
        "colab_type": "text"
      },
      "source": [
        "Note: 이 문서는 텐서플로 커뮤니티에서 번역했습니다. 커뮤니티 번역 활동의 특성상 정확한 번역과 최신 내용을 반영하기 위해 노력함에도\n",
        "불구하고 [공식 영문 문서](https://www.tensorflow.org/?hl=en)의 내용과 일치하지 않을 수 있습니다.\n",
        "이 번역에 개선할 부분이 있다면\n",
        "[tensorflow/docs](https://github.com/tensorflow/docs) 깃헙 저장소로 풀 리퀘스트를 보내주시기 바랍니다.\n",
        "문서 번역이나 리뷰에 참여하려면\n",
        "[docs-ko@tensorflow.org](https://groups.google.com/a/tensorflow.org/forum/#!forum/docs-ko)로\n",
        "메일을 보내주시기 바랍니다."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "UEu3q4jmpKVT"
      },
      "source": [
        "신경망을 구축하기 위해서 고수준 API인 `tf.keras`를 사용하길 권합니다. 대부분의 텐서플로 API는 즉시 실행(eager execution)과 함께 사용할 수 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "pwX7Fii1rwsJ",
        "colab": {}
      },
      "source": [
        "from __future__ import absolute_import, division, print_function, unicode_literals\n",
        "\n",
        "import tensorflow as tf\n",
        "\n",
        "tf.enable_eager_execution()"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "zSFfVVjkrrsI"
      },
      "source": [
        "## 층: 유용한 연산자 집합\n",
        "\n",
        "머신러닝을 위한 코드를 작성하는 대부분의 경우에 개별적인 연산과 변수를 조작하는 것보다는 높은 수준의 추상화에서 작업할 것입니다.\n",
        "\n",
        "많은 머신러닝 모델은 비교적 단순한 층(layer)을 조합하고 쌓아서 표현가능합니다. 또한 텐서플로는 여러 표준형 층을 제공하므로 사용자 고유의 응용 프로그램에 관련된 층을 처음부터 작성하거나, 기존 층의 조합으로 쉽게 만들 수 있습니다.\n",
        "\n",
        "텐서플로는 [전체 케라스](https://keras.io) API를 tf.keras 패키지에 포함하고 있습니다. 케라스 층은 모델을 구축하는데 매우 유용합니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "8PyXlPl-4TzQ",
        "colab": {}
      },
      "source": [
        "# tf.keras.layers 패키지에서 층은 객체입니다. 층을 구성하려면 간단히 객체를 생성하십시오.\n",
        "# 대부분의 layer는 첫번째 인수로 출력 차원(크기) 또는 채널을 취합니다.\n",
        "layer = tf.keras.layers.Dense(100)\n",
        "# 입력 차원의 수는 층을 처음 실행할 때 유추할 수 있기 때문에 종종 불필요합니다. \n",
        "# 일부 복잡한 모델에서는 수동으로 입력 차원의 수를 제공하는것이 유용할 수 있습니다.\n",
        "layer = tf.keras.layers.Dense(10, input_shape=(None, 5))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Fn69xxPO5Psr"
      },
      "source": [
        "미리 구성되어있는 층은 다음 [문서](https://www.tensorflow.org/api_docs/python/tf/keras/layers)에서 확인할 수 있습니다. Dense(완전 연결 층), Conv2D, LSTM, BatchNormalization, Dropout, 등을 포함하고 있습니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "E3XKNknP5Mhb",
        "colab": {}
      },
      "source": [
        "# 층을 사용하려면, 간단하게 호출합니다.\n",
        "layer(tf.zeros([10, 5]))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "Wt_Nsv-L5t2s",
        "colab": {}
      },
      "source": [
        "# layer는 유용한 메서드를 많이 가지고 있습니다. 예를 들어, `layer.variables`를 사용하여 층안에 있는 모든 변수를 확인할 수 있으며, \n",
        "# `layer.trainable_variables`를 사용하여 훈련가능한 변수를 확인할 수 있습니다. \n",
        "# 완전 연결(fully-connected)층은 가중치(weight)와 편향(biases)을 위한 변수를 가집니다. \n",
        "layer.variables"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "6ilvKjz8_4MQ",
        "colab": {}
      },
      "source": [
        "# 또한 변수는 객체의 속성을 통해 편리하게 접근가능합니다. \n",
        "layer.kernel, layer.bias"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "O0kDbE54-5VS"
      },
      "source": [
        "## 사용자 정의 층 구현\n",
        "사용자 정의 층을 구현하는 가장 좋은 방법은 tf.keras.Layer 클래스를 상속하고 다음과 같이 구현하는 것입니다.\n",
        "  *  `__init__` 에서 층에 필요한 매개변수를 입력 받습니다..\n",
        "  * `build`, 입력 텐서의 크기를 알고 나머지를 초기화 할 수 있습니다.\n",
        "  * `call`, 정방향 연산(forward computation)을 진행 할 수 있습니다.\n",
        "\n",
        "변수를 생성하기 위해 `build`가 호출되길 기다릴 필요가 없다는 것에 주목하세요. 또한 변수를 `__init__`에 생성할 수도 있습니다. 그러나 `build`에 변수를 생성하는 유리한 점은 층이 작동할 입력의 크기를 기준으로 나중에 변수를 만들 수 있다는 것입니다. 반면에, `__init__`에 변수를 생성하는 것은 변수 생성에 필요한 크기가 명시적으로 지정되어야 함을 의미합니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "5Byl3n1k5kIy",
        "colab": {}
      },
      "source": [
        "class MyDenseLayer(tf.keras.layers.Layer):\n",
        "  def __init__(self, num_outputs):\n",
        "    super(MyDenseLayer, self).__init__()\n",
        "    self.num_outputs = num_outputs\n",
        "    \n",
        "  def build(self, input_shape):\n",
        "    self.kernel = self.add_variable(\"kernel\", \n",
        "                                    shape=[int(input_shape[-1]), \n",
        "                                           self.num_outputs])\n",
        "    \n",
        "  def call(self, input):\n",
        "    return tf.matmul(input, self.kernel)\n",
        "  \n",
        "layer = MyDenseLayer(10)\n",
        "print(layer(tf.zeros([10, 5])))\n",
        "print(layer.trainable_variables)"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "tk8E2vY0-z4Z"
      },
      "source": [
        "다른 독자가 표준형 층의 동작을 잘 알고 있기 때문에, 가능한 경우 표준형 층을 사용하는것이 전체 코드를 읽고 유지하는데 더 쉽습니다. 만약 tf.keras.layers 또는 tf.contrib.layers에 없는 층을 사용하기 원하면 [깃허브](http://github.com/tensorflow/tensorflow/issues/new)에 이슈화하거나, 풀 리퀘스트(pull request)를 보내세요."
      ]
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "Qhg4KlbKrs3G"
      },
      "source": [
        "## 모델: 층 구성\n",
        "\n",
        "머신러닝 모델에서 대부분의 재미있는 많은 것들은 기존의 층을 조합하여 구현됩니다. 예를 들어, 레스넷(resnet)의 각 잔여 블록(residual block)은 합성곱(convolution), 배치 정규화(batch normalization), 쇼트컷(shortcut) 등으로 구성되어 있습니다. \n",
        "\n",
        "다른층을 포함한 모델을 만들기 위해 사용하는 메인 클래스는 tf.keras.Model입니다. 다음은 tf.keras.Model을 상속(inheritance)하여 구현한 코드입니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "N30DTXiRASlb",
        "colab": {}
      },
      "source": [
        "class ResnetIdentityBlock(tf.keras.Model):\n",
        "  def __init__(self, kernel_size, filters):\n",
        "    super(ResnetIdentityBlock, self).__init__(name='')\n",
        "    filters1, filters2, filters3 = filters\n",
        "\n",
        "    self.conv2a = tf.keras.layers.Conv2D(filters1, (1, 1))\n",
        "    self.bn2a = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "    self.conv2b = tf.keras.layers.Conv2D(filters2, kernel_size, padding='same')\n",
        "    self.bn2b = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "    self.conv2c = tf.keras.layers.Conv2D(filters3, (1, 1))\n",
        "    self.bn2c = tf.keras.layers.BatchNormalization()\n",
        "\n",
        "  def call(self, input_tensor, training=False):\n",
        "    x = self.conv2a(input_tensor)\n",
        "    x = self.bn2a(x, training=training)\n",
        "    x = tf.nn.relu(x)\n",
        "\n",
        "    x = self.conv2b(x)\n",
        "    x = self.bn2b(x, training=training)\n",
        "    x = tf.nn.relu(x)\n",
        "\n",
        "    x = self.conv2c(x)\n",
        "    x = self.bn2c(x, training=training)\n",
        "\n",
        "    x += input_tensor\n",
        "    return tf.nn.relu(x)\n",
        "\n",
        "    \n",
        "block = ResnetIdentityBlock(1, [1, 2, 3])\n",
        "print(block(tf.zeros([1, 2, 3, 3])))\n",
        "print([x.name for x in block.trainable_variables])"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "id": "yIbK5EiJyH_W",
        "colab_type": "text"
      },
      "source": [
        "그러나 대부분의 경우에, 많은 층으로 구성된 모델은 간단하게 연이어 하나의 층으로 호출할 수 있습니다. 이는 tf.keras.Sequential 사용하여 간단한 코드로 구현 가능합니다."
      ]
    },
    {
      "cell_type": "code",
      "metadata": {
        "colab_type": "code",
        "id": "L9frk7Ur4uvJ",
        "colab": {}
      },
      "source": [
        " my_seq = tf.keras.Sequential([tf.keras.layers.Conv2D(1, (1, 1)),\n",
        "                               tf.keras.layers.BatchNormalization(),\n",
        "                               tf.keras.layers.Conv2D(2, 1, \n",
        "                                                      padding='same'),\n",
        "                               tf.keras.layers.BatchNormalization(),\n",
        "                               tf.keras.layers.Conv2D(3, (1, 1)),\n",
        "                               tf.keras.layers.BatchNormalization()])\n",
        "my_seq(tf.zeros([1, 2, 3, 3]))"
      ],
      "execution_count": 0,
      "outputs": []
    },
    {
      "cell_type": "markdown",
      "metadata": {
        "colab_type": "text",
        "id": "c5YwYcnuK-wc"
      },
      "source": [
        "# 다음 단계\n",
        "\n",
        "이제 이전 노트북으로 돌아가서 선형 회귀 예제에 층과 모델을 사용하여 좀 더 나은 구조를 적용할 수 있습니다."
      ]
    }
  ]
}